package com.kvc.extend.papervision3d {
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.geom.Point;

	import org.papervision3d.cameras.Camera3D;
	import org.papervision3d.core.math.Matrix3D;
	import org.papervision3d.core.math.Number3D;
	import org.papervision3d.core.proto.CameraObject3D;
	import org.papervision3d.core.view.IView;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.render.BasicRenderEngine;
	import org.papervision3d.scenes.Scene3D;
	import org.papervision3d.view.AbstractView;
	import org.papervision3d.view.Viewport3D;	

	/**
	 * @author Kevin Cao
	 */
	public class FourCamerasView extends AbstractView implements IView {

		public static const PERSP : uint = 1;
		public static const FRONT : uint = 2;
		public static const LEFT : uint = 3;
		public static const TOP : uint = 4;

		protected var topCamera : Camera3D;
		protected var leftCamera : Camera3D;
		protected var frontCamera : Camera3D;
		protected var perspCamera : Camera3D;

		private var prevPoint : Point;

		private var aim : DisplayObject3D;

		private var _yaw : Number;
		private var _pitch : Number;

		public function get cameraAsCamera3D() : Camera3D {
			return perspCamera as Camera3D;
		}

		public function FourCamerasView(viewportWidth : Number = 640, viewportHeight : Number = 480, scaleToStage : Boolean = true, interactive : Boolean = false) {
			super();
			
			scene = new Scene3D();
			viewport = new Viewport3D(viewportWidth, viewportHeight, scaleToStage, interactive);
			addChild(viewport);
			renderer = new BasicRenderEngine();
			
			initFourCameras();
			
			addEventListener(Event.ADDED_TO_STAGE, setupEvents);
		}

		private function initFourCameras() : void {
			aim = DisplayObject3D.ZERO;
			
			topCamera = new Camera3D();
			topCamera.ortho = true;
			
			leftCamera = new Camera3D();
			leftCamera.ortho = true;
			
			frontCamera = new Camera3D();
			frontCamera.ortho = true;
			
			perspCamera = new Camera3D(55);
			perspCamera.target = aim;
			
			// 初始化相机内部数据
			topCamera.update(viewport.sizeRectangle);
			leftCamera.update(viewport.sizeRectangle);
			frontCamera.update(viewport.sizeRectangle);

			resetCameras();
			
			_camera = perspCamera;
		}

		public function resetCameras() : void {
			aim.transform.reset();
			
			topCamera.orthoScale = 0.3;
			topCamera.y = 2500;
			topCamera.z = 0;
			topCamera.lookAt(aim, new Number3D(0, 0, 1));

			leftCamera.orthoScale = 0.3;
			leftCamera.x = 2500;
			leftCamera.z = 0;
			leftCamera.lookAt(aim);
			
			frontCamera.orthoScale = 0.3;
			frontCamera.z = -2500;
			
			perspCamera.zoom = 40;
			perspCamera.x = 1000;
			perspCamera.y = 700;
			perspCamera.z = -1000;
			perspCamera.lookAt(aim);
			
			_pitch = 90 - perspCamera.rotationX;
			_yaw = 270 - perspCamera.rotationY;
		}

		private function setupEvents(evt : Event) : void {
			stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
			stage.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
			stage.addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
			stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
		}

		public function changeCamera(n : uint) : void {
			switch(n) {
				case FRONT:
					_camera = frontCamera;
					break;
				case LEFT:
					_camera = leftCamera;
					break;
				case TOP:
					_camera = topCamera;
					break;
				case PERSP:
				default:
					_camera = perspCamera;
					break;
			}
		}

		private function mouseDownHandler(evt : MouseEvent) : void {
			prevPoint = new Point(evt.stageX, evt.stageY);

			stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
		}

		private function mouseMoveHandler(evt : MouseEvent) : void {
			var dx : Number = prevPoint.x - evt.stageX;
			var dy : Number = prevPoint.y - evt.stageY;
			if(camera == perspCamera) {
				if(evt.ctrlKey) {
					camera.target.lookAt(camera);
					camera.moveRight(dx);
					camera.moveDown(dy);
					camera.target.moveLeft(dx);
					camera.target.moveDown(dy);
				} else if(evt.altKey) {
					var d : Number = dx * 2 + dy * 2;
					d = camera.distanceTo(camera.target)+d <= 0 ? 0.0001 : d;
					camera.moveBackward(d);
				} else {
					_pitch += dy * 0.2;
					_yaw += dx * 0.2;
					_pitch %= 360;
					_yaw %= 360;
					_pitch = Math.max(Math.min(179.9999, _pitch), 0.0001);
					camera.orbit(_pitch, _yaw, true);
				}
			} else {
				camera.orthoScale -= dx * 0.0008 + dy * 0.0008;
				camera.orthoScale = Math.max(camera.orthoScale, 0.0001);
			}
			prevPoint.x = evt.stageX;
			prevPoint.y = evt.stageY;
		}

		private function mouseUpHandler(evt : MouseEvent) : void {
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
		}

		private function mouseWheelHandler(evt : MouseEvent) : void {
			if(camera == perspCamera) {
				var d : Number = evt.delta * 20;
				d = camera.distanceTo(camera.target)+d <= 0 ? 0.0001 : d;
				camera.moveBackward(d);
			} else {
				camera.orthoScale -= evt.delta * 0.008;
				camera.orthoScale = Math.max(camera.orthoScale, 0.0001);
			}
		}

		private function keyUpHandler(evt : KeyboardEvent) : void {
			switch(String.fromCharCode(evt.charCode)) {
				case "p":
				case "1":
					changeCamera(PERSP);
					break;
				case "t":
				case "4":
					changeCamera(TOP);
					break;
				case "l":
				case "3":
					changeCamera(LEFT);
					break;
				case "f":
				case "2":
					changeCamera(FRONT);
					break;
				case "r":
				case "0":
					resetCameras();
					break;
			}
		}
	}
}
